package win.bigwhale.library.db.daoimp;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;

import win.bigwhale.library.db.dao.BaseDao;
import win.bigwhale.library.db.daohelper.WhaleDaoHelper;

public class BaseDaoImp<T> implements BaseDao<T> {
	public enum SqlOperation {
		INSERT, DELETE, UPDATE, SELECT;
	}

	private Class<T> EntityClass;
	private PreparedStatement statement;
	private String sql;
	private ArrayList<Object> argType;
	private ResultSet resultSet;

	@SuppressWarnings("unchecked")
	public BaseDaoImp() {
		ParameterizedType type = (ParameterizedType)getClass().getGenericSuperclass();
		EntityClass = (Class<T>) type.getActualTypeArguments()[0];
	}

	@Override
	public void add(T t) {
		doAddOrDeleteOrUpdate(t, SqlOperation.INSERT);
	}

	@Override
	public void delete(T t) {
		doAddOrDeleteOrUpdate(t, SqlOperation.DELETE);
	}

	@Override
	public void update(T t) {
		doAddOrDeleteOrUpdate(t, SqlOperation.UPDATE);
	}

	@Override
	public T select(T t) {
		sql = getSql(SqlOperation.SELECT);
		T obj = null;
		try {
			argType = setArg(t, SqlOperation.SELECT);
			statement = WhaleDaoHelper.getPreparedStatement(sql);
			statement = WhaleDaoHelper.setPreparedStatement(statement, argType);
			resultSet = statement.executeQuery();
			Field[] fields = EntityClass.getDeclaredFields();
			while (resultSet.next()) {
				obj = EntityClass.newInstance();
				for (int i = 0; i < fields.length; i++) {
					fields[i].setAccessible(true);
					fields[i].set(obj, resultSet.getObject(fields[i].getName()));
				}
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} catch (InstantiationException e) {
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} finally {
			WhaleDaoHelper.release(statement, resultSet);
		}
		return obj;
	}

	private ArrayList<Object> setArg(T t, SqlOperation so) {
		Field[] fields = EntityClass.getDeclaredFields();
		ArrayList<Object> objects = new ArrayList<>();
		try {
			for (Field field : fields) {
				field.setAccessible(true);
				objects.add(field.get(t));
			}
			Object idObject = objects.get(0);
			switch (so) {
			case INSERT:
				break;
			case UPDATE:
				objects.remove(0);
				objects.add(idObject);
				break;
			case DELETE:
				objects.clear();
				objects.add(idObject);
				break;
			case SELECT:
				objects.clear();
				objects.add(idObject);
				break;
			default:
				break;
			}
		} catch (IllegalArgumentException | IllegalAccessException e) {
			e.printStackTrace();
		}
		return objects;
	}

	private String getSql(SqlOperation so) {
		StringBuffer sql = new StringBuffer();
		Field[] fields = EntityClass.getDeclaredFields();

		switch (so) {
		case INSERT:
			sql.append("insert into " + EntityClass.getSimpleName());
			sql.append("(");
			for (Field field : fields) {
				field.setAccessible(true);
				String column = field.getName();
				sql.append(column).append(",");
			}
			sql.deleteCharAt(sql.length() - 1);
			sql.append(") value (");
			for (int i = 0; i < fields.length; i++) {
				sql.append("?,");
			}
			sql.deleteCharAt(sql.length() - 1);
			sql.append(")");
			break;
		case UPDATE:
			sql.append("update ").append(EntityClass.getSimpleName()).append(" set ");
			for (Field field : fields) {
				field.setAccessible(true);
				String column = field.getName();
				if (column.equals("id")) {
					continue;
				}
				sql.append(column).append("=").append("?,");
			}
			sql.deleteCharAt(sql.length() - 1);
			sql.append(" where id=?");
			break;
		case DELETE:
			sql.append("delete from ").append(EntityClass.getSimpleName()).append(" where id=?");
			break;
		case SELECT:
			sql.append("select * from ").append(EntityClass.getSimpleName()).append(" where id=?");
			break;
		default:
			break;
		}
		return sql.toString();
	}

	private void doAddOrDeleteOrUpdate(T t, SqlOperation so) {
		sql = getSql(so);
		try {
			argType = setArg(t, so);
			statement = WhaleDaoHelper.getPreparedStatement(sql);
			statement = WhaleDaoHelper.setPreparedStatement(statement, argType);
			statement.executeUpdate();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			WhaleDaoHelper.release(statement, resultSet);
		}
	}

}
